/*
 * Sylvain BERTRAND <digital.ragnarok@gmail.com>
 * code protected by GNU GPL v3
 */
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/sync.h>
#include <glib.h>
#include <libxml/xinclude.h>
#include "../toolkit/xml.h"
#include "../toolkit/display.h"
#include "../toolkit/xqueue.h"
#include "../toolkit/prop.h"
#include "misc.h"
#include "wm.h"
#include "window.h"
#include "geom.h"
#include "stacking.h"
#include "client.h"
#include "frame.h"
#include "config.h"
#include "place.h"
#include "moveresize.h"
#include "debug.h"
#include "grab.h"
static GHashTable *window_map;
static guint window_hash(Window *w)
{
	return *w;
}

static gboolean window_comp(Window *w1, Window *w2)
{
	return *w1 == *w2;
}

void window_startup(void)
{
	window_map = g_hash_table_new((GHashFunc)window_hash, (GEqualFunc)window_comp);
}

void window_shutdown(void)
{
	g_hash_table_destroy(window_map);
}

Window window_top(struct wm_window *self)
{
	switch (self->type) {
	case WM_WINDOW_CLASS_CLIENT:
		return WINDOW_AS_CLIENT(self)->frame->window;
	case WM_WINDOW_CLASS_INTERNAL:
		return WINDOW_AS_INTERNAL(self)->window;
	}
	g_assert_not_reached();
	return None;
}

enum wm_stacking_layer window_layer(struct wm_window *self)
{
	switch (self->type) {
	case WM_WINDOW_CLASS_CLIENT:
		return ((struct wm_client *)self)->layer;
	case WM_WINDOW_CLASS_INTERNAL:
		return WM_STACKING_LAYER_INTERNAL;
	}
	g_assert_not_reached();
	return None;
}

struct wm_window *window_find(Window xwin)
{
	return g_hash_table_lookup(window_map, &xwin);
}

void window_add(Window *xwin, struct wm_window *win)
{
	g_assert(xwin != NULL);
	g_assert(win != NULL);
	g_hash_table_insert(window_map, xwin, win);
}

void window_remove(Window xwin)
{
	g_assert(xwin != None);
	g_hash_table_remove(window_map, &xwin);
}

void window_manage_all(void)
{
	guint i, j, nchild;
	Window w, *children;
	XWMHints *wmhints;
	XWindowAttributes attrib;
	if(!XQueryTree(t_display, DefaultRootWindow(t_display), &w, &w, &children, &nchild)) {
		wm_debug("XQueryTree failed in window_manage_all");
		nchild = 0;
	}
	for(i = 0; i < nchild; i++) {
		if(children[i] == None)
			continue;
		wmhints = XGetWMHints(t_display, children[i]);
		if(wmhints) {
			if((wmhints->flags & IconWindowHint) && (wmhints->icon_window != children[i]))
				for(j = 0; j < nchild; j++)
					if(children[j] == wmhints->icon_window) {
						children[j] = None;
						break;
					}
			XFree(wmhints);
		}
	}
	for(i = 0; i < nchild; ++i) {
		if(children[i] == None)
			continue;
		if(window_find(children[i]))
			continue;
		if(XGetWindowAttributes(t_display, children[i], &attrib)) {
			if(attrib.map_state == IsUnmapped);
			else
				window_manage(children[i]);
		}
	}
	if(children)
		XFree(children);
}

static gboolean check_unmap(XEvent *e, gpointer data)
{
	const Window win = *(Window *)data;
	return ((e->type == DestroyNotify && e->xdestroywindow.window == win) || (e->type == UnmapNotify
			&& e->xunmap.window == win));
}

void window_manage(Window win)
{
	XWindowAttributes attrib;
	gboolean no_manage = FALSE;
	Window icon_win = None;
	grab_server(TRUE);
	if(xqueue_exists_local(check_unmap, &win)) {
		wm_debug("Trying to manage unmapped window. Aborting that.");
		no_manage = TRUE;
	} else if(!XGetWindowAttributes(t_display, win, &attrib))
		no_manage = TRUE;
	else {
		XWMHints *wmhints;
		if((wmhints = XGetWMHints(t_display, win))) {
			if((wmhints->flags & StateHint) && wmhints->initial_state == WithdrawnState) {
				if(wmhints->flags & IconWindowHint)
					icon_win = wmhints->icon_window;
			}
			XFree(wmhints);
		}
	}
	if(!no_manage) {
		if(attrib.override_redirect) {
			wm_debug("not managing override redirect window 0x%x", win);
			grab_server(FALSE);
		} else
			client_manage(win);
	} else {
		grab_server(FALSE);
		wm_debug("FAILED to manage window 0x%x", win);
	}
}

void window_unmanage_all(void)
{
	client_unmanage_all();
}
